package main

import (
	"context"
	"flag"
	"fmt"
	"net/url"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strings"

	consts "qrsj-update/const"
	"qrsj-update/utils"

	"github.com/imroc/req/v3"
	jsoniter "github.com/json-iterator/go"

	"github.com/wailsapp/wails/v2/pkg/runtime"
)

var (
	// 默认更新地址
	defaultUpdateUrl = "https://wwvm.lanzouj.com/b00wm1cr5g"
	// 默认访问密码
	defaultPwd = "9527"
	// 默认要下载第几个文件(从0开始)
	defaultFileIndex = 0
)

// App struct
type App struct {
	ctx    context.Context
	json   jsoniter.API
	client *req.Client

	startKey      string // 启动秘钥
	updateUrl     string // 更新地址
	updatePwd     string // 访问密码
	updateIndex   int    // 要下载第几个文件(从0开始)
	sourceAppName string // 源应用程序名称

	downloadAppPath string // 下载的应用程序路径

	fidValue string // fid值
	uidValue string // uid值
	tValue   string // t值
	kValue   string // k值
}

type UpdateNoticeResult struct {
	Notice  string `json:"notice"`
	Version string `json:"version"`
	Msg     string `json:"msg"`
	Code    int    `json:"code"`
}

type FileListResult struct {
	Info interface{} `json:"info"`
	Text interface{} `json:"text"`
}

type FileDownloadUrlResult struct {
	Dom string      `json:"dom"`
	Url interface{} `json:"url"`
	Inf interface{} `json:"inf"`
}

// NewApp creates a new App application struct
func NewApp() *App {
	return &App{}
}

// startup is called when the app starts. The context is saved
// so we can call the runtime methods
func (a *App) startup(ctx context.Context) {
	a.ctx = ctx
	a.json = jsoniter.ConfigCompatibleWithStandardLibrary
	a.client = req.C().SetUserAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36")
	a.GetStartInfo()
}

// 获取应用名称
func (a *App) GetAppName() string {
	return consts.APP_NAME
}

// 获取启动信息
func (a *App) GetStartInfo() {
	flag.StringVar(&a.startKey, "startKey", "", "key")
	flag.StringVar(&a.updateUrl, "updateUrl", defaultUpdateUrl, "更新地址")
	flag.StringVar(&a.updatePwd, "updatePwd", defaultPwd, "访问密码")
	flag.IntVar(&a.updateIndex, "updateIndex", defaultFileIndex, "文件索引")
	flag.StringVar(&a.sourceAppName, "sourceAppName", "qrsj-assistant.exe", "源应用程序名称")
	flag.Parse()

	// 模拟命令行参数
	// a.startKey = "qrsj-update1379605323@qq.com"
	// a.updateUrl = "https://wwvm.lanzouj.com/b00wm1cs0h"
	// a.updatePwd = "9527"
	// a.updateIndex = 0
	// a.sourceAppName = "qrsj-assistant.exe"
	fmt.Println("启动秘钥:", a.startKey)
	fmt.Println("更新地址:", a.updateUrl)
	fmt.Println("访问密码:", a.updatePwd)
	fmt.Println("文件索引:", a.updateIndex)
	fmt.Println("源应用程序名称:", a.sourceAppName)
	if a.startKey != consts.StartKey {
		fmt.Println("启动秘钥错误")
		runtime.Quit(a.ctx)
	}
}

// 获取更新公告和版本号
func (a *App) GetUpdateNotice() string {
	var noticeResult UpdateNoticeResult
	resp, err := a.client.R().Get(a.updateUrl)
	if err != nil {
		errMsg := fmt.Sprintf("版本号获取失败: %s", err.Error())
		fmt.Println(errMsg)
		noticeResult.Msg = errMsg
		noticeResult.Code = 0
		noticJson, _ := a.json.MarshalIndent(noticeResult, "", "  ")
		return string(noticJson)
	}
	respStr, _ := resp.ToString()
	fidPattern := regexp.MustCompile(`'fid':(\d+)`)
	uidPattern := regexp.MustCompile(`'uid':'(\d+)'`)
	appNamePattern := regexp.MustCompile(`<title>(.+)</title>`)
	appDescPattern := regexp.MustCompile(`<span id="filename">(.+)</span>`)
	tVarPattern := regexp.MustCompile(`'t':(\w+)`)
	kVarPattern := regexp.MustCompile(`'k':(\w+)`)
	tVarName := ""
	kVarName := ""
	if tVarMatch := tVarPattern.FindStringSubmatch(respStr); tVarMatch != nil {
		tVarName = tVarMatch[1]
	}
	if kVarMatch := kVarPattern.FindStringSubmatch(respStr); kVarMatch != nil {
		kVarName = kVarMatch[1]
	}
	tValuePattern := regexp.MustCompile(fmt.Sprintf(`var %s = '(\d+)'`, tVarName))
	kValuePattern := regexp.MustCompile(fmt.Sprintf(`var %s = '([a-f0-9]+)'`, kVarName))
	appFolderName := ""
	if fidMatch := fidPattern.FindStringSubmatch(respStr); fidMatch != nil {
		a.fidValue = fidMatch[1]
	}
	if uidMatch := uidPattern.FindStringSubmatch(respStr); uidMatch != nil {
		a.uidValue = uidMatch[1]
	}
	if appNameMatch := appNamePattern.FindStringSubmatch(respStr); appNameMatch != nil {
		// appNameMatch[1]取到的内容是appName_version
		appFolderName = strings.Split(appNameMatch[1], "_")[0]
		noticeResult.Version = strings.Split(appNameMatch[1], "_")[1]
	}
	if appDescMatch := appDescPattern.FindStringSubmatch(respStr); appDescMatch != nil {
		noticeResult.Notice = appDescMatch[1]
	}
	if tValueMatch := tValuePattern.FindStringSubmatch(respStr); tValueMatch != nil {
		a.tValue = tValueMatch[1]
	}
	if kValueMatch := kValuePattern.FindStringSubmatch(respStr); kValueMatch != nil {
		a.kValue = kValueMatch[1]
	}
	fmt.Println("文件夹名称:", appFolderName)
	fmt.Println("版本号:", noticeResult.Version)
	fmt.Println("公告:", noticeResult.Notice)
	if appFolderName == "" && noticeResult.Version == "" {
		noticeResult.Msg = "版本号获取失败"
		noticeResult.Code = 0
	} else {
		noticeResult.Msg = "版本号获取成功"
		noticeResult.Code = 1
	}
	noticJson, _ := a.json.MarshalIndent(noticeResult, "", "  ")
	return string(noticJson)
}

// 更新应用程序
func (a *App) UpdateApp() string {
	fmt.Println("开始更新应用程序")
	parsedURL, _ := url.Parse(a.updateUrl)
	updateDomain := parsedURL.Scheme + "://" + parsedURL.Host
	fmt.Println("更新域名:", updateDomain)
	// 获取文件列表
	var fileListResult FileListResult
	resp, err := a.client.R().SetPathParam("fid", a.fidValue).SetFormDataAnyType(map[string]interface{}{
		"lx":  2,
		"fid": a.fidValue,
		"uid": a.uidValue,
		"pg":  1,
		"rep": 0,
		"t":   a.tValue,
		"k":   a.kValue,
		"up":  1,
		"ls":  1,
		"pwd": a.updatePwd,
	}).SetSuccessResult(&fileListResult).
		Post(updateDomain + "/filemoreajax.php?file={fid}")
	if err != nil {
		fmt.Println("err1", err)
		return err.Error()
	}
	if !resp.IsSuccessState() {
		fmt.Println("获取文件列表失败，状态码:", resp.GetStatus())
		return "获取文件列表失败: " + resp.GetStatus()
	}
	fmt.Println("fileListResult", fileListResult)

	appId := ""
	appName := ""
	appSize := ""
	if fileListResult.Info == "sucess" {
		if records, ok := fileListResult.Text.([]interface{}); ok && len(records) > 0 {
			if record, ok := records[a.updateIndex].(map[string]interface{}); ok {
				appId = record["id"].(string)
				appName = record["name_all"].(string)
				appSize = record["size"].(string)
			}
		} else {
			fmt.Println("文件列表为空")
			return "文件列表为空"
		}
	}
	fmt.Println("文件id:", appId)
	fmt.Println("文件名称:", appName)
	fmt.Println("文件大小:", appSize)

	// 获取src
	resp, err = a.client.R().Get(updateDomain + "/" + appId)
	if err != nil {
		fmt.Println("err2", err)
		return err.Error()
	}
	respStr, err := resp.ToString()
	if err != nil {
		fmt.Println("err3", err)
		return err.Error()
	}
	fmt.Println("respStr", respStr)
	srcPattern := regexp.MustCompile(`src="(.+)" frameborder`)
	srcValue := ""
	if srcMatch := srcPattern.FindStringSubmatch(respStr); srcMatch != nil {
		srcValue = srcMatch[1]
	}
	srcUrl := updateDomain + srcValue
	// fmt.Println("srcUrl", srcUrl)
	resp, err = a.client.R().Get(srcUrl)
	if err != nil {
		fmt.Println("err4", err)
		return err.Error()
	}
	respStr, err = resp.ToString()
	if err != nil {
		fmt.Println("err5", err)
		return err.Error()
	}
	ajaxmPattern := regexp.MustCompile(`url : '(.+)'`)
	actionPattern := regexp.MustCompile(`'action':'(.+)','signs'`)
	signsPattern := regexp.MustCompile(`ajaxdata = '(.+)'`)
	signPattern := regexp.MustCompile(`sign':'(.+)','websign`)
	signKeyPattern := regexp.MustCompile(`aihidcms = '(.+)'`)
	websignPattern := regexp.MustCompile(`ciucjdsdc = '(.+)'`)
	vesPattern := regexp.MustCompile(`ves':(.+),'kd'`)
	kdPattern := regexp.MustCompile(`kdns =(.+);`)
	ajaxmValue := ""
	actionValue := ""
	signsValue := ""
	signValue := ""
	signKeyValue := ""
	websignValue := ""
	vesValue := ""
	kdValue := ""
	if ajaxmMatch := ajaxmPattern.FindStringSubmatch(respStr); ajaxmMatch != nil {
		ajaxmValue = ajaxmMatch[1]
		ajaxmValue = strings.Replace(ajaxmValue, "/ajaxm.php?file=", "", -1)
	}
	if actionMatch := actionPattern.FindStringSubmatch(respStr); actionMatch != nil {
		actionValue = actionMatch[1]
	}
	if signsMatch := signsPattern.FindStringSubmatch(respStr); signsMatch != nil {
		signsValue = signsMatch[1]
	}
	if signMatch := signPattern.FindStringSubmatch(respStr); signMatch != nil {
		signValue = signMatch[1]
	}
	if signKeyMatch := signKeyPattern.FindStringSubmatch(respStr); signKeyMatch != nil {
		signKeyValue = signKeyMatch[1]
	}
	if websignMatch := websignPattern.FindStringSubmatch(respStr); websignMatch != nil {
		websignValue = websignMatch[1]
	}
	if vesMatch := vesPattern.FindStringSubmatch(respStr); vesMatch != nil {
		vesValue = vesMatch[1]
	}
	if kdMatch := kdPattern.FindStringSubmatch(respStr); kdMatch != nil {
		kdValue = kdMatch[1]
	}
	fmt.Println("ajaxmValue", ajaxmValue)
	fmt.Println("actionValue", actionValue)
	fmt.Println("signsValue", signsValue)
	fmt.Println("signValue", signValue)
	fmt.Println("signKeyValue", signKeyValue)
	fmt.Println("websignValue", websignValue)
	fmt.Println("vesValue", vesValue)
	fmt.Println("kdValue", kdValue)

	// 获取最终下载地址
	var fileDownloadUrlResult FileDownloadUrlResult
	resp, err = a.client.R().SetHeaders(map[string]string{
		"content-type": "application/x-www-form-urlencoded",
		"referer":      srcUrl,
	}).SetPathParam("ajaxmValue", ajaxmValue).SetFormDataAnyType(map[string]interface{}{
		"action":     actionValue,
		"signs":      signsValue,
		"sign":       signValue,
		"websign":    websignValue,
		"websignkey": signKeyValue,
		"ves":        vesValue,
		"kd":         kdValue,
	}).SetSuccessResult(&fileDownloadUrlResult).Post(updateDomain + "/ajaxm.php?file={ajaxmValue}")
	if err != nil {
		fmt.Println("err6", err)
		return err.Error()
	}
	if !resp.IsSuccessState() {
		fmt.Println("获取最终下载地址失败，状态码:", resp.GetStatus())
	}
	// fmt.Println("resp", resp)
	dom := ""
	url := ""
	if value, ok := fileDownloadUrlResult.Inf.(float64); ok && value == 0 {
		dom = fileDownloadUrlResult.Dom
		url = fileDownloadUrlResult.Url.(string)
	}
	downloadUrl := dom + "/file/" + url
	// fmt.Println("downloadUrl", downloadUrl)

	// 下载文件
	callback := func(info req.DownloadInfo) {
		progress := float64(info.DownloadedSize) / float64(info.Response.ContentLength) * 100.0
		if info.Response.Response != nil {
			fmt.Printf("当前下载进度 %.2f%%\n", progress)
			runtime.EventsEmit(a.ctx, "update_progress", progress)
		}
	}
	dir, _ := os.Getwd()
	path := filepath.Join(dir, appName+".temp")
	a.downloadAppPath = path
	resp, err = a.client.R().
		SetOutputFile(path).
		SetHeaders(map[string]string{
			"accept-language": "zh-CN,zh;q=0.9",
		}).
		SetDownloadCallback(callback).
		Get(downloadUrl)
	if err != nil {
		fmt.Println("err7", err)
		return err.Error()
	}

	if resp.IsSuccessState() {
		fmt.Println("下载成功")
		return ""
	} else {
		fmt.Println("下载失败")
		return "下载失败"
	}
}

// 替换应用
func (a *App) ReplaceApp() {
	// 删除sourceAppName
	dir, _ := os.Getwd()
	path := filepath.Join(dir, a.sourceAppName)
	fmt.Println("源应用程序路径", path)
	// 先判断源应用程序是否存在
	exists := utils.FileExists(path)
	fmt.Println("源应用程序是否存在", exists)
	if exists {
		// 删除源应用程序
		_ = os.Remove(path)
		// 将下载的应用程序后缀.temp去掉
		newPath := strings.Replace(a.downloadAppPath, ".temp", "", -1)
		// 重命名下载的应用程序
		_ = os.Rename(a.downloadAppPath, newPath)
		// 启动新应用程序
		cmd := exec.Command(newPath, "--isUpdate", "1")
		err := cmd.Start()
		if err != nil {
			fmt.Println("启动新应用程序失败", err)
		} else {
			fmt.Println("启动新应用程序成功")
			runtime.Quit(a.ctx)
		}
	}
}
